use darling::{FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use proc_macro2::TokenStream as T2;
use syn::{parse_macro_input, DeriveInput};

use crate::periph2::rcc::{self, RccAxB};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(delay))]
pub struct TimeDelayConfig {
    #[darling(default)]
    ms: Option<String>,
    us: String,
    tim: Timer,
    init: String,
}

impl TimeDelayConfig {

    fn build(&self) -> T2 {
        let name = self.tim.name();
        let fn_us = format_ident!("{}", self.us);
        let fn_init = format_ident!("{}", self.init);
        let rcc_field = format_ident!("{}en", name);
        let rcc_src = match self.tim {
            Timer::TIM1 | Timer::TIM8 => rcc::rcc_enable(RccAxB::APB2, &rcc_field, false),
            _ => rcc::rcc_enable(RccAxB::APB1, &rcc_field, false),
        };
        let name = format_ident!("{}", name.to_uppercase());
        
        let ms_src = if let Some(ms) = self.ms.as_deref() {
            let fn_ms = format_ident!("{}", ms);
            quote! {
                pub fn #fn_ms(ms: u32) {
                    #[inline(always)]
                    fn #fn_us(us: u16) {
                        let tim: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                        tim.cnt.write(|w| w.cnt().variant(0));
                        while tim.cnt.read().cnt().bits() < us {}
                    }

                    for _ in 0..ms {
                        #fn_us(1000);
                    }
                }
            }
        } else {
            quote! {}
        };

        quote! {
            pub fn #fn_init() {
                #rcc_src
                let tim: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                tim.cr1.reset();
                tim.cr2.reset();
                tim.arr.write(|w| w.arr().bits(0xFFFF));
                tim.psc.write(|w| w.psc().variant(72 - 1));
                tim.egr.write(|w| w.ug().update());
                tim.cr1.write(|w| w.cen().enabled());
            }

            pub fn #fn_us(us: u16) {
                let tim: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                tim.cnt.write(|w| w.cnt().variant(0));
                while tim.cnt.read().cnt().bits() < us {}
            }

            #ms_src
        }
    }

}

#[derive(Debug, FromMeta)]
pub enum Timer {
    #[darling(rename = "TIM1")]
    TIM1,
    #[darling(rename = "TIM2")]
    TIM2,
    #[darling(rename = "TIM3")]
    TIM3,
    #[darling(rename = "TIM4")]
    TIM4,
    #[darling(rename = "TIM5")]
    TIM5,
    #[darling(rename = "TIM6")]
    TIM6,
    #[darling(rename = "TIM7")]
    TIM7,
    #[darling(rename = "TIM8")]
    TIM8,
}

impl Timer {

    pub fn name(&self) -> &'static str {
        match self {
            Timer::TIM1 => "tim1",
            Timer::TIM2 => "tim2",
            Timer::TIM3 => "tim3",
            Timer::TIM4 => "tim4",
            Timer::TIM5 => "tim5",
            Timer::TIM6 => "tim6",
            Timer::TIM7 => "tim7",
            Timer::TIM8 => "tim8",
        }
    }

    pub fn is_advanced(&self) -> bool {
        match self {
            Timer::TIM1 | Timer::TIM8 => true,
            _ => false
        }
    }
}

pub fn build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let config = TimeDelayConfig::from_derive_input(&input).unwrap();
    let build = config.build();
    let expand = quote! {
        impl #name {
            #build
        }
    };
    expand.into()
}
